package cn.ondraw.ragflow.service.impl;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import cn.ondraw.ragflow.utils.RagFlowEncryptUtil;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
@RequiredArgsConstructor
public class TokenManager {

    private static final String PYTHON_API_URL = "http://1.95.79.99:8038";
    private static final String REDIS_TOKEN_KEY = "ragflow:token";
    private static final String REDIS_LOCK_KEY = "ragflow:token:lock";
    private static final long TOKEN_EXPIRE_TIME = 60; // token过期时间，单位秒
    private static final long LOCK_EXPIRE_TIME = 10; // 锁过期时间，单位秒

    private final RedisTemplate<String, String> redisTemplate;

    /**
     * 从缓存获取token
     */
    public String getToken() {
        String cachedToken = redisTemplate.opsForValue().get(REDIS_TOKEN_KEY);
        if (cachedToken == null) {
            log.info("缓存中未找到token，尝试刷新");
            refreshToken();
            cachedToken = redisTemplate.opsForValue().get(REDIS_TOKEN_KEY);
            if (cachedToken == null) {
                throw new RuntimeException("获取token失败，请稍后重试");
            }
        }
        return cachedToken;
    }

    /**
     * 获取新token并存入缓存
     */
    public void refreshToken() {
        // 尝试获取分布式锁
        Boolean locked = redisTemplate.opsForValue().setIfAbsent(REDIS_LOCK_KEY, "1", LOCK_EXPIRE_TIME, TimeUnit.SECONDS);
        if (!Boolean.TRUE.equals(locked)) {
            log.info("其他线程正在刷新token，跳过本次刷新");
            return;
        }

        try {
            // 双重检查，避免其他线程已经刷新了token
            String existingToken = redisTemplate.opsForValue().get(REDIS_TOKEN_KEY);
            if (existingToken != null) {
                log.info("token已存在，无需刷新");
                return;
            }

            redisTemplate.opsForValue().set(REDIS_TOKEN_KEY, fetchNewToken(), TOKEN_EXPIRE_TIME, TimeUnit.SECONDS);
            log.info("新token已存入缓存，过期时间：{}秒", TOKEN_EXPIRE_TIME);
        } catch (Exception e) {
            log.error("刷新token失败", e);
            throw new RuntimeException("刷新token失败: " + e.getMessage());
        } finally {
            // 释放锁
            redisTemplate.delete(REDIS_LOCK_KEY);
        }
    }

    private String fetchNewToken() throws IOException {
        String requestUrl = PYTHON_API_URL + "/v1/user/login";
        Map<String, String> body = new HashMap<>();
        body.put("email", "13823590772@139.com");
        // 使用RSA加密密码
        String encryptedPassword = RagFlowEncryptUtil.rsaEncrypt("admin123");
        body.put("password", encryptedPassword);
        String requestJson = JSONUtil.toJsonStr(body);

        HttpPost httpPost = new HttpPost(requestUrl);
        httpPost.setEntity(new StringEntity(requestJson, StandardCharsets.UTF_8));
        httpPost.setHeader("Content-type", "application/json");
        httpPost.setHeader("Accept", "application/json");

        HttpResponse response = HttpClients.createDefault().execute(httpPost);
        String result = EntityUtils.toString(response.getEntity());

        // 解析响应获取access_token
        JSONObject jsonObject = JSONUtil.parseObj(result);
        if (jsonObject.getInt("code", -1) != 0) {
            throw new RuntimeException("获取token失败: " + jsonObject.getStr("message", "未知错误"));
        }

        // 从响应头中获取authorization
        String authorization = response.getFirstHeader("Authorization").getValue();
        log.info("<UNK>token: {}<UNK>", authorization);
        if (authorization == null || authorization.isEmpty()) {
            throw new RuntimeException("响应头中未找到Authorization");
        }
        return authorization;
    }
}
